/*
*   scheduler.c
*   Team: Moose Commando
*   Authors: Dustin Striplin, Bill Minter
*   the simulator, contains the ui & is updated in real time when an event occurs.
*/

#include <pthread.h>
#include <time.h>
#include <ncurses.h>
#include "scheduler.h"
pthread_mutex_t updateMTX; //use for updating the gui
WINDOW *mainWin;
WINDOW *readyQ;
WINDOW *curProcess;
WINDOW *intProcess;
WINDOW *devices;
WINDOW *requestIDwin;
WINDOW *typewin;
WINDOW *mutwin;
WINDOW *condwin;
WINDOW *teamwin;
WINDOW *keywin;
WINDOW *p1;
WINDOW *p2;
WINDOW *p3;
WINDOW *p4;
WINDOW *p5;
int RQsize;
int mutsize;
int condsize;
int compBoundRange;
FILE *ofp;

/*
*   Update the gui with the contents of the packet
*/
void updateGui(PacketPtr packet) {

    if(packet != (PacketPtr) NULL) {
        //get update mutex lock so more than one thread won't try updating at the same time.
        pthread_mutex_lock(&updateMTX);
        //update gui


        wclear(mainWin); //clears before repainting

        init_pair(1,COLOR_BLACK, COLOR_RED);
        init_pair(2,COLOR_BLACK, COLOR_GREEN);
        init_pair(3,COLOR_BLACK, COLOR_WHITE);
        init_pair(4,COLOR_BLACK, COLOR_YELLOW);
        init_pair(5,COLOR_BLACK, COLOR_MAGENTA);


        curProcess = subwin(stdscr,4, 20, 2, 2);
        box(curProcess,0,0);
        mvprintw(2, 4,"Running Process:");
        mvwprintw(curProcess,2,2, "       %d",packet->runningProcess);
        if(packet->runningProcess < compBoundRange) {
            wbkgd(curProcess,COLOR_PAIR(1));
        } else if(packet->runningProcess < compBoundRange + 2) {
            wbkgd(curProcess,COLOR_PAIR(2));
        } else if(packet->runningProcess < compBoundRange + 3) {
            wbkgd(curProcess,COLOR_PAIR(3));
        } else if(packet->runningProcess < compBoundRange + mutsize + 3) {
            wbkgd(curProcess,COLOR_PAIR(4));
        } else if(packet->runningProcess < compBoundRange + 2*mutsize + 3) {
            wbkgd(curProcess,COLOR_PAIR(5));
        }
        wrefresh(curProcess);


        typewin = subwin(stdscr,4, 20, 2, 24);
        box(typewin,0,0);
        mvprintw(2, 26, "Type:");


        switch(packet->type) {
            case 0:
                mvwprintw(typewin,2,2, "NOTHING");
                break;
            case 1:
                mvwprintw(typewin,2,2, "IO_REQUEST");
                break;
            case 2:
                mvwprintw(typewin,2,2, "IO_INT");
                break;
            case 3:
                mvwprintw(typewin,2,2, "TIMER_INT");
                break;
            case 4:
                mvwprintw(typewin,2,2, "LOCK_MTX");
                break;
            case 5:
                mvwprintw(typewin,2,2, "UNLOCK_MTX");
                break;
            case 6:
                mvwprintw(typewin,2,2, "WAIT");
                break;
            case 7:
                mvwprintw(typewin,2,2, "SIGNAL");
                break;
            case 8:
                mvwprintw(typewin,2,2, "KEYBOARD_REQUEST");
                break;
            case 9:
                mvwprintw(typewin,2,2, "READ");
                break;
            case 10:
                mvwprintw(typewin,2,2, "WRITE");
                break;
            case 11:
                mvwprintw(typewin,2,2, "CHECK_FULL");
                break;
            case 12:
                mvwprintw(typewin,2,2, "CHECK_EMPTY");
                break;
        }

        wrefresh(typewin);

        requestIDwin = subwin(stdscr,4, 18, 2,46);
        box(requestIDwin,0,0);
        mvprintw(2, 48, "Request ID:");

        if(packet->requestID == -1) {
            mvwprintw(requestIDwin,2,2,"    None");
        } else {
            mvwprintw(requestIDwin,2,2,"      %d", packet->requestID);
        }

        wrefresh(requestIDwin);


        int j = 0;

        teamwin = subwin(stdscr, 4, 74, 2, 70);
        mvwprintw(teamwin,0,2,"                        - Team Moose Commando - ");
        mvwprintw(teamwin,2,2,"Dustin Striplin, Sangeeta Bhattacharjee, Bill Minter, and Wilson Burhan");
        wrefresh(teamwin);

        readyQ = subwin(stdscr,4, RQsize, 8, 2);
        box(readyQ,0,0);
        mvprintw(8, 4, "Ready Queue:");
        mvwprintw(readyQ,2,2, "Head | ");

        wrefresh(readyQ);



        for(; j < packet->readyQueueSize; j++) {
            wprintw(readyQ,"%d | ", packet->readyQueue[j]);
        }
        wprintw(readyQ, "Idle | Tail");
        wrefresh(readyQ);
     //   printf("\n");

        devices = subwin(stdscr, 8, 30, 14, 2);
        box(devices,0,0);
        mvwprintw(devices,0,2, "Devices:");
        int dy = 2;

        int i = 0;
        for(; i < NUM_IO; i++) {  //print devices
            int index = 0;
            int id = packet->deviceQueues[i][index];
            mvwprintw(devices,dy,2,"Device #%d: ",i);
            while(id != (int) NULL && id != NO_ID) {
                wprintw(devices,"%d", id);
                index++;
                id = packet->deviceQueues[i][index];
            }
            dy = dy + 2;
        }

        wrefresh(devices);

        mutwin = subwin(stdscr, (mutsize*2) + 2, 30, 24, 2);
        box(mutwin,0,0);
        mvprintw(24, 4, "Mutexes:");
        wrefresh(mutwin);

        dy = 2;
        //print mutexes
        i = 0;
        for(; i < packet->numProducerConsumer; i++) {
            int index = 0;
            int id = packet->mutexQueues[i][index];
            if(packet->mutexOwners[i] == -1) {
                mvwprintw(mutwin,dy,2,"Mutex# %d, no owner", i);
            } else {
                mvwprintw(mutwin,dy,2,"Mutex# %d, owner %d:  ", i, packet->mutexOwners[i]);
            }
            while(id != (int) NULL && id != NO_ID) {
                wprintw(mutwin,"%d", id);
                index++;
                id = packet->mutexQueues[i][index];
            }
            dy = dy + 2;
        }
        wrefresh(mutwin);

        keywin = subwin(stdscr, 23, 40, 14, 86);
        box(keywin, 0, 0);
        mvprintw(14,88,"Process color key:");

        p1 = subwin(stdscr, 3,36,16,88);
        wbkgd(p1,COLOR_PAIR(1));
        mvwprintw(p1,1,7,"Compute Bound Process");
        wrefresh(p1);

        p2 = subwin(stdscr, 3,36,20,88);
        wbkgd(p2,COLOR_PAIR(2));
        mvwprintw(p2,1,12,"I/O Process");
        wrefresh(p2);

        p3 = subwin(stdscr, 3,36,24,88);
        wbkgd(p3,COLOR_PAIR(3));
        mvwprintw(p3,1,8,"Keyboard I/O Process");
        wrefresh(p3);

        p4 = subwin(stdscr, 3,36,28,88);
        wbkgd(p4,COLOR_PAIR(4));
        mvwprintw(p4,1,10,"Producer Process");
        wrefresh(p4);

        p5 = subwin(stdscr, 3,36,32,88);
        wbkgd(p5,COLOR_PAIR(5));
        mvwprintw(p5,1,10,"Consumer Process");
        wrefresh(p5);

        wrefresh(keywin);

        condwin = subwin(stdscr, (condsize*2) + 2, 30, 14, 34);
        box(condwin,0,0);
        mvprintw(14,36,"Condition Variables:");

        dy = 2;
        //print condition variables
        i = 0;
        for(; i < packet->numProducerConsumer*2; i++) {
            int index = 0;
            int id = packet->conditionQueues[i][index];
            mvwprintw(condwin,dy,2,"CV# %d: ", i);
            while(id != (int) NULL && id != NO_ID) {
                wprintw(condwin,"%d ", id);
                index++;
                id = packet->conditionQueues[i][index];
            }
      //      printf("\n");
            dy = dy + 2;
        }
        wrefresh(condwin);

     //////////////////////////////////print to file/////////////////////////////////////

      //update gui
        fprintf(ofp, "type = %d\n", packet->type);
        fprintf(ofp, "requestID = %d\n", packet->requestID);
        fprintf(ofp, "currently running = %d\n", packet->runningProcess);

        j = 0;
        fprintf(ofp, "Ready Queue: ");  //print ready queue

        for(; j < packet->readyQueueSize; j++) {
            fprintf(ofp, "%d, ", packet->readyQueue[j]);
        }
        fprintf(ofp, "\n");

        i = 0;
        for(; i < NUM_IO; i++) {  //print devies
            int index = 0;
            int id = packet->deviceQueues[i][index];
            fprintf(ofp, "Device# %d: ",i);
            while(id != (int) NULL && id != NO_ID) {
                fprintf(ofp, "%d, ", id);
                index++;
                id = packet->deviceQueues[i][index];
            }
            fprintf(ofp, "\n");
        }

        //print mutexes
        i = 0;
        for(; i < packet->numProducerConsumer; i++) {
            int index = 0;
            int id = packet->mutexQueues[i][index];
            fprintf(ofp, "Mutex# %d, owner %d:  ", i, packet->mutexOwners[i]);
            while(id != (int) NULL && id != NO_ID) {
                fprintf(ofp, "%d, ", id);
                index++;
                id = packet->mutexQueues[i][index];
            }
            fprintf(ofp, "\n");
        }

        //print condition variables
        i = 0;
        for(; i < packet->numProducerConsumer*2; i++) {
            int index = 0;
            int id = packet->conditionQueues[i][index];
            fprintf(ofp, "CV# %d: ", i);
            while(id != (int) NULL && id != NO_ID) {
                fprintf(ofp, "%d, ", id);
                index++;
                id = packet->conditionQueues[i][index];
            }
            fprintf(ofp, "\n");
        }

        if(packet->consumersValue != NO_ID)
            fprintf(ofp, "shared value = %d\n", packet->consumersValue);

        fprintf(ofp, "\n");

        pthread_mutex_unlock(&updateMTX);//release update mutex lock

        packet->destruct(packet);//destroy the packet
    }

}

//has a loop that calls cpuStep, cpuStep will return a packet that we use to update the gui
//arguments are in the form of -cb 8 -pc 4
int main(int argc, char*argv[]) {
    //////////////////////////////PARSE ARGUMENTS///////////////////////////////////////
    long numCompute, numProdCon, numCycle=0;
    numCompute = strtol(argv[2], NULL, 0);
    numProdCon = strtol(argv[4], NULL, 0);
    numCycle = strtol(argv[6], NULL, 0);

    if (numCycle == 0)
        numCycle = 10000;

    ////////////////////////INITIALIZE PROGRAM/////////////////////////////////////////////////

    pthread_mutex_init(&updateMTX, NULL);//initialize ready queue's mutex
    srand(time(NULL));//seed random number generator
    PacketPtr packet; //for storing meta data about the last thing that ran.
    unsigned int count = 0; //the number of times the loop has run
    unsigned int current_step = 0;//the current number of time ticks
    int numThreads = NUM_IO + 1; //IO threads, 1 timer thread, 1 keyboard thread
    CPUPtr cpu = cpuConstructor(numCompute,numProdCon, updateGui);

    /////////////////////////initializ curses////////////////////////////////////////////////////


    initscr();          /* Start curses mode        */

    mainWin = stdscr;

    mutsize = numProdCon;
    condsize = 2*numProdCon;
    compBoundRange = numCompute;
    RQsize = (((3+numCompute+(2*numProdCon))*5) + 10);
    ofp = fopen("schedulerOut.txt", "w");
    start_color();

    ////////////////////////////RUN THE PROGRAM//////////////////////////////////////////////////
    while(current_step < numCycle) {
        if(count % MOD_NUM == 0) {
            PacketPtr newPacket = cpu->step(cpu);
            if(newPacket != (PacketPtr)NULL)     //a null packet means there is nothing to update
                updateGui(newPacket);
            current_step++;
        }
        count++;
    }

    ////////////////////////////CLEAN UP FOR ENDING PROGRAM///////////////////////////////////////
    cpu->isRunning = FALSE;  //turn on cpu so the threads will finish

    wakeIOThreads(cpu);//wake all threads that may have been waiting on conditions.
                        //do this after setting isRunning to false, so the threads don't
                        //try to do more work.

    //join all threads so the cpu can be destructed
    int i = 0;
    int errorCode = 0;
    for(;i < numThreads; i++) {
        errorCode = pthread_join(cpu->threads[i], NULL);
        if(errorCode)
            printf("Error joining pthread %d, error code = %d\n", i, errorCode);
    }
    mvprintw(40, 86, "Simulation completed. Press enter to terminate display.");
    refresh();
    getchar();
    endwin();
    cpu->destruct(cpu);
}
